﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Threading;

namespace ControlLibrary.Controls;

/// <summary>
///     自定义日期时间范围选择器控件
/// </summary>
[TemplatePart(Name = "PART_StartCalendar", Type = typeof(Calendar))]
[TemplatePart(Name = "PART_EndCalendar", Type = typeof(Calendar))]
[TemplatePart(Name = "PART_ClearButton", Type = typeof(Button))]
[TemplatePart(Name = "PART_ConfirmButton", Type = typeof(Button))]
[TemplatePart(Name = "PART_OpenButton", Type = typeof(Button))]
[TemplatePart(Name = "PART_DateTimeRanger", Type = typeof(TextBox))]
public class DateTimeRangePicker : Control
{
    /// <summary>
    ///     日期时间范围开始属性
    /// </summary>
    public static readonly DependencyProperty DateTimeRangeStartProperty =
        DependencyProperty.Register(nameof(DateTimeRangeStart), typeof(DateTime), typeof(DateTimeRangePicker),
            new FrameworkPropertyMetadata(DateTime.Now, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                OnSelectedDateTimeChanged));

    /// <summary>
    ///     日期时间范围结束属性
    /// </summary>
    public static readonly DependencyProperty DateTimeRangeEndProperty =
        DependencyProperty.Register(nameof(DateTimeRangeEnd), typeof(DateTime), typeof(DateTimeRangePicker),
            new FrameworkPropertyMetadata(DateTime.Now.AddDays(1),
                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedDateTimeChanged));

    /// <summary>
    ///     最小值属性
    /// </summary>
    public static readonly DependencyProperty MinValueProperty =
        DependencyProperty.Register(nameof(MinValue), typeof(DateTime), typeof(DateTimeRangePicker),
            new PropertyMetadata(DateTime.MinValue, SetMinMaxValue));

    /// <summary>
    ///     最大值属性
    /// </summary>
    public static readonly DependencyProperty MaxValueProperty =
        DependencyProperty.Register(nameof(MaxValue), typeof(DateTime), typeof(DateTimeRangePicker),
            new PropertyMetadata(DateTime.MaxValue, SetMinMaxValue));

    /// <summary>
    ///     弹出框是否打开属性
    /// </summary>
    internal static readonly DependencyProperty IsPopupOpenProperty =
        DependencyProperty.Register(nameof(IsPopupOpen), typeof(bool), typeof(DateTimeRangePicker),
            new PropertyMetadata(false));

    /// <summary>
    ///     开始小时索引属性
    /// </summary>
    internal static readonly DependencyProperty StartHourIndexProperty =
        DependencyProperty.Register(nameof(StartHourIndex), typeof(int), typeof(DateTimeRangePicker),
            new PropertyMetadata(0));

    /// <summary>
    ///     开始分钟索引属性
    /// </summary>
    internal static readonly DependencyProperty StartMinuteIndexProperty =
        DependencyProperty.Register(nameof(StartMinuteIndex), typeof(int), typeof(DateTimeRangePicker),
            new PropertyMetadata(0));

    /// <summary>
    ///     结束小时索引属性
    /// </summary>
    internal static readonly DependencyProperty EndHourIndexProperty =
        DependencyProperty.Register(nameof(EndHourIndex), typeof(int), typeof(DateTimeRangePicker),
            new PropertyMetadata(0));

    /// <summary>
    ///     结束分钟索引属性
    /// </summary>
    internal static readonly DependencyProperty EndMinuteIndexProperty =
        DependencyProperty.Register(nameof(EndMinuteIndex), typeof(int), typeof(DateTimeRangePicker),
            new PropertyMetadata(0));

    // 结束日历控件
    private Calendar? _endCalendar;

    // 第一次点击的日期
    private DateTime? _firstClickDate;

    // 悬停结束日期
    private DateTime _hoverEnd;

    // 悬停开始日期
    private DateTime _hoverStart;

    // 开始日历控件
    private Calendar? _startCalendar;

    static DateTimeRangePicker()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(DateTimeRangePicker),
            new FrameworkPropertyMetadata(typeof(DateTimeRangePicker)));
    }

    /// <summary>
    ///     开始小时索引属性
    /// </summary>
    internal int StartHourIndex
    {
        get => (int)GetValue(StartHourIndexProperty);
        set => SetValue(StartHourIndexProperty, value);
    }

    /// <summary>
    ///     开始分钟索引属性
    /// </summary>
    internal int StartMinuteIndex
    {
        get => (int)GetValue(StartMinuteIndexProperty);
        set => SetValue(StartMinuteIndexProperty, value);
    }

    /// <summary>
    ///     结束小时索引属性
    /// </summary>
    internal int EndHourIndex
    {
        get => (int)GetValue(EndHourIndexProperty);
        set => SetValue(EndHourIndexProperty, value);
    }

    /// <summary>
    ///     结束分钟索引属性
    /// </summary>
    internal int EndMinuteIndex
    {
        get => (int)GetValue(EndMinuteIndexProperty);
        set => SetValue(EndMinuteIndexProperty, value);
    }

    /// <summary>
    ///     日期时间范围开始属性
    /// </summary>
    public DateTime DateTimeRangeStart
    {
        get => (DateTime)GetValue(DateTimeRangeStartProperty);
        set => SetValue(DateTimeRangeStartProperty, value);
    }

    /// <summary>
    ///     日期时间范围结束属性
    /// </summary>
    public DateTime DateTimeRangeEnd
    {
        get => (DateTime)GetValue(DateTimeRangeEndProperty);
        set => SetValue(DateTimeRangeEndProperty, value);
    }

    /// <summary>
    ///     最小值属性
    /// </summary>
    public DateTime MinValue
    {
        get => (DateTime)GetValue(MinValueProperty);
        set => SetValue(MinValueProperty, value);
    }

    /// <summary>
    ///     最大值属性
    /// </summary>
    public DateTime MaxValue
    {
        get => (DateTime)GetValue(MaxValueProperty);
        set => SetValue(MaxValueProperty, value);
    }

    /// <summary>
    ///     弹出框是否打开属性
    /// </summary>
    internal bool IsPopupOpen
    {
        get => (bool)GetValue(IsPopupOpenProperty);
        set => SetValue(IsPopupOpenProperty, value);
    }

    private static void OnSelectedDateTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is not DateTimeRangePicker picker) return;
        if (e.NewValue is not DateTime date) return;
        var newDate = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, 0);
        if (date == newDate) return;
        picker.SetCurrentValue(e.Property, newDate);
        var bindingExpression = BindingOperations.GetBindingExpression(d, e.Property);
        if (bindingExpression is not null)
        {
            picker.Dispatcher.BeginInvoke(DispatcherPriority.DataBind,
                new Action(() => { bindingExpression.UpdateSource(); }));
        }
    }


    /// <summary>
    ///     当控件应用模板时调用
    /// </summary>
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        // 获取模板部件
        _startCalendar = GetTemplateChild("PART_StartCalendar") as Calendar;
        _endCalendar = GetTemplateChild("PART_EndCalendar") as Calendar;

        // 挂接事件处理程序
        if (_startCalendar != null)
        {
            _startCalendar.SelectedDatesChanged += Calendar_SelectedDatesChanged;
            _startCalendar.PreviewMouseUp += Calendar_PreviewMouseUp;
        }

        if (_endCalendar != null)
        {
            _endCalendar.SelectedDatesChanged += Calendar_SelectedDatesChanged;
            _endCalendar.PreviewMouseUp += Calendar_PreviewMouseUp;
        }

        if (GetTemplateChild("PART_ClearButton") is Button clearButton)
        {
            clearButton.Click += ClearButton_Click;
        }

        if (GetTemplateChild("PART_ConfirmButton") is Button confirmButton)
        {
            confirmButton.Click += ConfirmButton_Click;
        }

        if (GetTemplateChild("PART_DateTimeRanger") is TextBox dateTimeRanger)
        {
            dateTimeRanger.GotMouseCapture += (_, _) => OpenPicker();
        }

        if (GetTemplateChild("PART_OpenButton") is Button openButton)
        {
            openButton.Click += (_, _) => OpenPicker();
        }

        UpdateBlackoutDates();
    }

    /// <summary>
    ///     日历预览鼠标抬起事件处理
    /// </summary>
    private static void Calendar_PreviewMouseUp(object sender, MouseButtonEventArgs e)
    {
        if (Mouse.Captured is CalendarItem)
        {
            Mouse.Capture(null);
        }
    }


    /// <summary>
    ///     设置最小最大值属性的回调方法
    /// </summary>
    private static void SetMinMaxValue(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is DateTimeRangePicker picker)
        {
            picker.UpdateBlackoutDates();
        }
    }

    /// <summary>
    ///     更新不可选择的日期
    /// </summary>
    private void UpdateBlackoutDates()
    {
        if (_startCalendar == null || _endCalendar == null) return;

        _startCalendar.BlackoutDates.Clear();
        _endCalendar.BlackoutDates.Clear();
        var min = Min(MinValue, MaxValue);
        var max = Max(MinValue, MaxValue);
        CalendarDateRange range;
        if ((min - DateTime.MinValue).TotalDays > 1)
        {
            range = new CalendarDateRange(DateTime.MinValue, min.AddDays(-1));
            _startCalendar.BlackoutDates.Add(range);
            _endCalendar.BlackoutDates.Add(range);
        }

        if ((DateTime.MaxValue - max).TotalDays < 1) return;
        range = new CalendarDateRange(max.AddDays(1), DateTime.MaxValue);
        _startCalendar.BlackoutDates.Add(range);
        _endCalendar.BlackoutDates.Add(range);
    }

    private static DateTime Min(DateTime a, DateTime b)
    {
        return a > b ? b : a;
    }

    private static DateTime Max(DateTime a, DateTime b)
    {
        return a > b ? a : b;
    }

    /// <summary>
    ///     日历选中日期变更事件处理
    /// </summary>
    private void Calendar_SelectedDatesChanged(object? sender, SelectionChangedEventArgs e)
    {
        if (e.AddedItems.Count == 0 || e.AddedItems[0] is not DateTime date)
            return;
        if (_firstClickDate == null)
        {
            _firstClickDate = date;
            _hoverStart = _hoverEnd = date;
        }
        else
        {
            if (_firstClickDate > date)
            {
                _hoverStart = date;
                _hoverEnd = _firstClickDate.Value;
            }
            else
            {
                _hoverStart = _firstClickDate.Value;
                _hoverEnd = date;
            }

            _firstClickDate = null;
        }

        UpdateCalendarSelection();
    }

    /// <summary>
    ///     更新日历选择
    /// </summary>
    private void UpdateCalendarSelection()
    {
        if (_startCalendar == null || _endCalendar == null) return;
        if (_hoverStart == default || (_hoverEnd == default) | (_hoverStart > _hoverEnd)) return;

        _startCalendar.SelectedDatesChanged -= Calendar_SelectedDatesChanged;
        _endCalendar.SelectedDatesChanged -= Calendar_SelectedDatesChanged;
        try
        {
            _startCalendar.SelectedDates.Clear();
            _endCalendar.SelectedDates.Clear();
            _startCalendar.SelectedDates.AddRange(_hoverStart, _hoverEnd);
            _endCalendar.SelectedDates.AddRange(_hoverStart, _hoverEnd);
        }
        finally
        {
            _startCalendar.SelectedDatesChanged += Calendar_SelectedDatesChanged;
            _endCalendar.SelectedDatesChanged += Calendar_SelectedDatesChanged;
        }
    }

    /// <summary>
    ///     确认按钮点击事件处理
    /// </summary>
    private void ConfirmButton_Click(object sender, RoutedEventArgs e)
    {
        if (_hoverStart == default || _hoverEnd == default)
            return;

        var start = CombineDateTime(_hoverStart, StartHourIndex, StartMinuteIndex);

        var end = CombineDateTime(_hoverEnd, EndHourIndex, EndMinuteIndex);

        DateTimeRangeStart = start < end ? start : end;
        DateTimeRangeEnd = start < end ? end : start;

        IsPopupOpen = false;
    }

    /// <summary>
    ///     组合日期时间
    /// </summary>
    private static DateTime CombineDateTime(DateTime date, int hours, int minutes)
    {
        return date.Date.AddHours(hours).AddMinutes(minutes);
    }

    /// <summary>
    ///     打开日期选择器
    /// </summary>
    private void OpenPicker()
    {
        _firstClickDate = null;
        _hoverStart = DateTimeRangeStart;
        _hoverEnd = DateTimeRangeEnd;

        StartHourIndex = DateTimeRangeStart.Hour;
        StartMinuteIndex = DateTimeRangeStart.Minute;
        EndHourIndex = DateTimeRangeEnd.Hour;
        EndMinuteIndex = DateTimeRangeEnd.Minute;


        if (_startCalendar != null) _startCalendar.DisplayDate = DateTimeRangeStart;
        UpdateCalendarSelection();
        IsPopupOpen = true;
    }

    /// <summary>
    ///     清除按钮点击事件处理
    /// </summary>
    private void ClearButton_Click(object sender, RoutedEventArgs e)
    {
        _hoverStart = _hoverEnd = default;

        EndMinuteIndex = EndHourIndex = StartMinuteIndex = StartHourIndex = 0;


        UpdateCalendarSelection();
        IsPopupOpen = false;
    }
}